package com.optitronics.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.optitronics.beans.Administrator;
import com.optitronics.beans.Product;
import com.optitronics.beans.Reseller;
import com.optitronics.beans.ResellerOrder;
import com.optitronics.service.UserService;
import com.optitronics.ui.ChangePasswordForm;
import com.optitronics.util.OptitronicsConstants;
import com.optitronics.util.OptitronicsUtils;

@Service("optitronicsUserService")
public class UserServiceImpl implements UserDetailsService, UserService {

	private Log logger = LogFactory.getLog(this.getClass());

	/**
	 * Tries to find admin for that username, if not found tries to find
	 * reseller for that username
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

		logger.info("Trying to load user for username: " + username);

		UserDetails userDetails = null;

		Administrator administrator = Administrator.findByUsername(username);
		if (administrator != null) {
			logger.info("Administrator found for that username");

			List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
			authorities.add(new GrantedAuthorityImpl(OptitronicsConstants.ROLE_ADMIN));
			userDetails = new User(username, administrator.getPassword(), true, true, true, true, authorities);
		} else {
			Reseller reseller = Reseller.findResellerByEmail(username);
			if (reseller != null) {
				logger.info("Reseller found for that username");
				List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
				authorities.add(new GrantedAuthorityImpl(OptitronicsConstants.ROLE_RESELLER));

				boolean enabled = Reseller.STATUS_ACTIVE.equals(reseller.getStatus());
				userDetails = new User(username, reseller.getPassword(), enabled, true, true, true, authorities);
			}
		}

		return userDetails;
	}

	/**
	 * Changing password for logged user
	 * 
	 * @param formObj
	 */
	public void changePassword(ChangePasswordForm formObj) {
		String username = OptitronicsUtils.getActiveUser();
		logger.info("Changing password for: " + username);
		Md5PasswordEncoder encoder = new Md5PasswordEncoder();

		String md5Paswword = encoder.encodePassword(formObj.getPassword(), null);
		if (OptitronicsUtils.isUserAdmin()) {
			Administrator admin = Administrator.findByUsername(username);
			admin.setPassword(md5Paswword);
			admin.merge();
		} else if (OptitronicsUtils.isUserReseller()) {
			Reseller reseller = Reseller.findResellerByEmail(username);
			reseller.setPassword(md5Paswword);
			reseller.merge();
		}
	}

	/**
	 * Checking if reseller is trying to see someone others orders
	 * 
	 * @param ownerId
	 */
	public void checkUserResellerOrders(Integer ownerId) {
		boolean accessGranted = true;
		if (OptitronicsUtils.isUserReseller() && ownerId != null) {
			logger.info(OptitronicsUtils.getLoggerPrefix() + ": " + "Checking is ownerId: " + ownerId
					+ " belongs to logged user");

			String activeUser = OptitronicsUtils.getActiveUser();
			Reseller reseller = Reseller.findResellerByEmail(activeUser);
			accessGranted = reseller.getId().equals(ownerId);
		}

		if (!accessGranted) {
			throw new AccessDeniedException("Access denied");
		}
	}

	/**
	 * Checks if product is active
	 * 
	 * @param productId
	 */
	public void checkProductActive(Integer productId) {
		boolean accessGranted = true;

		Product product = Product.findProduct(productId);
		if (product == null) {
			accessGranted = false;
		} else if (!Boolean.TRUE.equals(product.getActive())) {
			accessGranted = false;
		}

		if (!accessGranted) {
			throw new AccessDeniedException("Access denied");
		}
	}

	/**
	 * Checking if reseller is trying to see his order
	 * 
	 * @param orderId
	 */
	public void checkUserResellerOrder(Integer orderId) {
		// TODO trebalo bi role voter ili tako nesto ali slucaj je prost pa nema
		// potrebe za tim
		boolean accessGranted = true;
		if (OptitronicsUtils.isUserReseller()) {
			logger.info(OptitronicsUtils.getLoggerPrefix() + ": " + "Checking is order: " + orderId
					+ " belongs to logged user");

			ResellerOrder order = ResellerOrder.findResellerOrder(orderId);
			if (order != null) {
				String activeUser = OptitronicsUtils.getActiveUser();
				String resellerEmail = order.getReseller().getEmail();
				accessGranted = activeUser.equals(resellerEmail);
			} else {
				accessGranted = false;
			}
		}

		if (!accessGranted) {
			throw new AccessDeniedException("Access denied");
		}
	}
}
